[SwiftUI] Canvasを使ってグラデーションを描画。DragGestureで動かす

[SwiftUI] Canvasを使ってグラデーションを描画。DragGestureで動かす

Clock Icon2024.09.03

こんにちは。きんくまです。SwiiftUI勉強中です。

今回作ったもの

https://www.youtube.com/shorts/1X7de9Ac82o

240903_canvas_gradient

Canvas上でドラッグすると、グラデーションの中心点が移動します

ソースコード

struct ContentView: View {

    @State private var dragPoint: CGPoint = .zero
    private let canvasPadding: CGFloat = 30
    private let dragPointRadius: CGFloat = 10
    private var dragPointDiameter: CGFloat {
        dragPointRadius * 2
    }

    func dragFunc(canvasSize: CGSize) -> some Gesture {
        DragGesture()
            .onChanged { value in
                // 中心点がCavnvasの範囲内におさまるようにする
                var newLocation = value.location
                if newLocation.x < 0 {
                    newLocation.x = 0
                }
                if newLocation.x > canvasSize.width {
                    newLocation.x = canvasSize.width
                }
                if newLocation.y < 0 {
                    newLocation.y = 0
                }
                if newLocation.y > canvasSize.height {
                    newLocation.y = canvasSize.height
                }

                dragPoint = CGPoint(x: newLocation.x, y: newLocation.y)
            }
            .onEnded { value in

            }
    }

    var body: some View {
        VStack {
            GeometryReader { proxy in
                Canvas { context, size in

                    // グラデーション
                    context.fill(
                        Path(roundedRect: .init(x: 0, y: 0, width: size.width, height: size.height), cornerRadius: 0),

                        with: .radialGradient(
                            Gradient(stops: [
                                .init(color: .red, location: 0),
                                .init(color: .orange, location: 0.4),
                                .init(color: .yellow, location: 0.49),
                                .init(color: .white, location: 0.5),
                                .init(color: .yellow, location: 0.51),
                                .init(color: .black, location: 1)
                            ]), center: .init(x: dragPoint.x, y: dragPoint.y), startRadius: 0, endRadius: size.height / 4 + dragPoint.y * 0.6)
                    )

                    // 中心点の描画
                    context.fill(
                        Path(ellipseIn: .init(origin: CGPoint(x: dragPoint.x - dragPointRadius, y: dragPoint.y - dragPointRadius), size: .init(width: dragPointDiameter, height: dragPointDiameter))),
                        with: .color(.white)
                    )
                    context.stroke(
                        Path(ellipseIn: .init(origin: CGPoint(x: dragPoint.x - dragPointRadius, y: dragPoint.y - dragPointRadius), size: .init(width: dragPointDiameter, height: dragPointDiameter))),
                        with: .color(.gray), style: .init(lineWidth: 1))
                }
            }
            .gesture(dragFunc(canvasSize: proxy.size))
        }.padding(canvasPadding)
    }
}

ドラッグする

.gestureモディファイアーでドラッグイベントを受け取ります。
内部でCanvasサイズを知りたかったので、GeometryReaderでCanvasを囲みました

.gesture(dragFunc(canvasSize: proxy.size))

ドラッグ中は、中心点をCanvas外に出さないようにxとy座標をおさまるようにしています

    func dragFunc(canvasSize: CGSize) -> some Gesture {
        DragGesture()
            .onChanged { value in
                // 中心点がCavnvasの範囲内におさまるようにする
                var newLocation = value.location
                if newLocation.x < 0 {
                    newLocation.x = 0
                }
                //以下略

グラデーション

Canvasではなく通常のShape系でグラデーションの方法は解説があったのですが、Canvasでのグラデーション方法が見つからなかったので、仕様書をみつつ、うまくいくまでビルドエラーと向き合いました

仕様書
https://developer.apple.com/documentation/swiftui/canvas

せっかくなので下の方にドラッグするとグラデーションが大きくなるようにしてみました

何でCanvas使うの?

リアルタイムに描画処理が更新される場合、負荷が高まるだろうなという想定です。
通常の図形で描画するよりも、Canvasの方が負荷が低くなりそうだったので、Canvasを使いました

感想

昔はこういうのをたくさん書いていたので、非常に懐かしく楽しかったです

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.